package com.qiaofang.applet.service.member.service.impl;

import com.qiaofang.applet.common.base.MongoBaseService;
import com.qiaofang.applet.common.context.QfContext;
import com.qiaofang.applet.common.context.QfContextHolder;
import com.qiaofang.applet.constants.BusinessConstants;
import com.qiaofang.applet.service.member.domain.MemberFocus;
import com.qiaofang.applet.service.member.repository.MemberFocusRepository;
import com.qiaofang.applet.service.member.repository.MemberRepository;
import com.qiaofang.applet.service.member.service.MemberFocusService;
import com.qiaofang.applet.service.member.service.dto.MemberEmpDto;
import com.qiaofang.applet.service.member.service.dto.MemberFocusQueryDto;
import com.qiaofang.applet.service.member.service.dto.MemberFocusSimpleDto;
import com.qiaofang.applet.service.other.util.DateTypeUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.domain.*;
import org.springframework.data.mongodb.core.MongoTemplate;
import org.springframework.data.mongodb.core.aggregation.Aggregation;
import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.repository.support.PageableExecutionUtils;
import org.springframework.stereotype.Service;
import org.springframework.util.CollectionUtils;

import java.util.*;
import java.util.stream.Collectors;

/**
 * @author daiyuxiang
 */
@Slf4j
@Service
public class MemberFocusServiceImpl extends MongoBaseService<MemberFocus, String> implements MemberFocusService {

    private final MemberFocusRepository memberFocusRepository;

    @Autowired
    private MongoTemplate mongoTemplate;

    @Autowired
    private MemberRepository memberRepository;

    @Autowired
    public MemberFocusServiceImpl(MemberFocusRepository repository) {
        super(repository);
        this.memberFocusRepository = repository;
    }

    @Override
    public List<String> findBusinessListByParams(MemberFocusQueryDto queryDto) {
        Query query = new Query();

        QfContext qfContext = QfContextHolder.getContext();

        Criteria criteria = Criteria.where("corporationCode").is(qfContext.getCorporationCode());

        query.addCriteria(criteria);

        if (StringUtils.isNotBlank(queryDto.getMemberId())) {
            criteria.and("memberId").is(queryDto.getMemberId());
        }
        if (StringUtils.isNotBlank(queryDto.getBusinessType())) {
            criteria.and("businessType").is(queryDto.getBusinessType());
        }

        query.with(Sort.by(
                Sort.Order.desc("createTime")
        ));

        List<MemberFocus> result = mongoTemplate.find(query, MemberFocus.class);

        return result.stream().map(MemberFocus::getBusinessUuid).collect(Collectors.toList());
    }

    @Override
    public Page<MemberFocus> findBusinessListByParams(MemberFocusQueryDto queryDto, Pageable pageable) {
        Query query = new Query();

        QfContext qfContext = QfContextHolder.getContext();

        Criteria criteria = Criteria.where("corporationCode").is(qfContext.getCorporationCode());
        criteria.and("cityCode").is(qfContext.getCityCode());
        query.addCriteria(criteria);

        if (StringUtils.isNotBlank(queryDto.getMemberId())) {
            criteria.and("memberId").is(queryDto.getMemberId());
        }
        if (StringUtils.isNotBlank(queryDto.getBusinessType())) {
            criteria.and("businessType").is(queryDto.getBusinessType());
        }

        // 拼分页参数
        PageRequest pageRequest = PageRequest.of(pageable.getPageNumber(), pageable.getPageSize(), Sort.Direction.DESC, "createTime");
        query.with(pageRequest);

        long count = mongoTemplate.count(query, MemberFocus.class);
        List<MemberFocus> result = new ArrayList<>();
        if (count > 0) {
            result = mongoTemplate.find(query, MemberFocus.class);
        }

        return PageableExecutionUtils.getPage(result, pageable, () -> count);
    }

    @Override
    public void deleteByMemberIdAndBusinessUuidAndBusinessType(String memberId, String businessUuid, String businessType) {
        memberFocusRepository.deleteByMemberIdAndBusinessUuidAndBusinessType(memberId, businessUuid, businessType);
    }

    @Override
    public Boolean isExistsByMemberIdAndBusinessUuid(String memberId, String businessUuid, String businessType) {
        ExampleMatcher matcher = ExampleMatcher.matching()
                .withMatcher("memberId", match -> match.exact())
                .withMatcher("businessUuid", match -> match.exact())
                .withMatcher("corporationCode", match -> match.exact());
        MemberFocus params = new MemberFocus();

        if(StringUtils.isNotEmpty(businessType)) {
            params.setBusinessType(businessType);
            matcher = matcher.withMatcher("businessType", match -> match.exact());
        }
        matcher = matcher.withIgnoreNullValues();
        params.setMemberId(memberId);
        params.setBusinessUuid(businessUuid);
        params.setCorporationCode(QfContextHolder.getContext().getCorporationCode());
        params.setCityCode(QfContextHolder.getContext().getCityCode());
        Optional<MemberFocus> optional = memberFocusRepository.findOne(Example.of(params, matcher));

        if (optional.isPresent()) {
            return true;
        } else {
            return false;
        }
    }

    @Override
    public Map<String, MemberFocusSimpleDto> getFocusReportData(Date date, String corporationCode) {
        Criteria criteria = Criteria.where("corporationCode").is(corporationCode)
                .and("createTime").gte(DateTypeUtil.getDayZero(date)).lt(DateTypeUtil.getDayZero(DateTypeUtil.getTomorrow(date)));
        Criteria criteriaFans = new Criteria().andOperator(criteria, Criteria.where("businessType").is(BusinessConstants.EMPLOYEE.getValue()));
        Criteria criteriaFocus = new Criteria().andOperator(criteria, Criteria.where("businessType").is(BusinessConstants.MEMBER.getValue()));
        Aggregation aggFans = Aggregation.newAggregation(
                Aggregation.match(criteriaFans),
                Aggregation.group("businessUuid")
                        .first("businessUuid").as("employeeUniqueUuid")
                        .count().as("fansNum")
        );
        List<MemberFocusSimpleDto> fansList = mongoTemplate.aggregate(aggFans, "member_member_focus", MemberFocusSimpleDto.class).getMappedResults();
        Map<String, MemberFocusSimpleDto> fansNumMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(fansList)) {
            fansNumMap = fansList.stream().collect(Collectors.toMap(MemberFocusSimpleDto::getEmployeeUniqueUuid, a -> a, (k1, k2) -> k1));
        }

        Aggregation aggFocus = Aggregation.newAggregation(
                Aggregation.match(criteriaFocus),
                Aggregation.group("memberId")
                        .first("memberId").as("employeeUniqueUuid")
                        .count().as("focusNum")
        );
        List<MemberFocusSimpleDto> focusList = mongoTemplate.aggregate(aggFocus, "member_member_focus", MemberFocusSimpleDto.class).getMappedResults();
        focusList = new ArrayList<>(focusList);
        //memberId转employeeUniqueUuid
        List<String> memberIds = focusList.stream().map(MemberFocusSimpleDto::getEmployeeUniqueUuid).collect(Collectors.toList());
        List<MemberEmpDto> memberEmpDtos = memberRepository.findAllByIdIn(memberIds);
        Map<String, String> memberEmpMap = memberEmpDtos.stream().collect(Collectors.toMap(MemberEmpDto::getId, MemberEmpDto::getEmployeeUniqueUuid, (k1, k2) -> k1));
        Iterator<MemberFocusSimpleDto> iterator = focusList.iterator();
        while (iterator.hasNext()){
            MemberFocusSimpleDto simpleDto = iterator.next();
            String employeeUniqueUuid = memberEmpMap.get(simpleDto.getEmployeeUniqueUuid());
            if (StringUtils.isEmpty(employeeUniqueUuid)){
                iterator.remove();
                continue;
            }
            simpleDto.setEmployeeUniqueUuid(employeeUniqueUuid);
        }
        Map<String, MemberFocusSimpleDto> focusNumMap = new HashMap<>();
        if (!CollectionUtils.isEmpty(focusList)) {
            focusNumMap = focusList.stream().collect(Collectors.toMap(MemberFocusSimpleDto::getEmployeeUniqueUuid, a -> a, (k1, k2) -> k1));
        }

        Set<Map.Entry<String, MemberFocusSimpleDto>> entries = fansNumMap.entrySet();
        for (Map.Entry<String, MemberFocusSimpleDto> entry : entries) {
            String employeeUniqueUuid = entry.getKey();
            MemberFocusSimpleDto simpleDto = entry.getValue();
            MemberFocusSimpleDto focusSimpleDto = focusNumMap.get(employeeUniqueUuid);
            if (focusSimpleDto != null){
                simpleDto.setFocusNum(focusSimpleDto.getFocusNum());
                focusNumMap.remove(employeeUniqueUuid);
            }
        }
        fansNumMap.putAll(focusNumMap);
        return fansNumMap;
    }
}
